In [ ]:
epochs = 10
# We don't use the whole dataset for efficiency purpose, but feel free to increase these numbers
n_train_items = 640
n_test_items = 640

भाग X - MNIST वरील सुरक्षित प्रशिक्षण आणि मूल्यांकन (Secure Training and Evaluation on MNIST)

सर्व्हिस सोल्यूशन म्हणून मशीन लर्निंग (MLaaS) तयार करताना, कंपनीला त्याचे मॉडेल प्रशिक्षित करण्यासाठी इतर भागीदारांकडील डेटामध्ये प्रवेश करण्याची विनंती करण्याची आवश्यकता असू शकते. आरोग्य किंवा वित्त यामध्ये मॉडेल आणि डेटा दोन्ही महत्त्वाचे आहेतः मॉडेल पॅरामीटर एक व्यवसायिक मालमत्ता आहे, तर डेटा वैयक्तिक डेटा असतो जो घट्टपणे नियमन केला जातो.

या संदर्भात, एक संभाव्य उपाय म्हणजे मॉडेल आणि डेटा दोन्ही एन्क्रिप्ट करणे आणि एन्क्रिप्टेड अंकांपेक्षा अधिक मशीन लर्निंग मॉडेल्सचे प्रशिक्षण देणे. हे हमी देते की कंपनी उदाहरणार्थ रूग्णाच्या वैद्यकीय नोंदींमध्ये प्रवेश करणार नाही आणि आरोग्य सुविधा ते ज्या मॉडेलमध्ये योगदान देतात त्या मॉडेलची तपासणी करू शकणार नाहीत. बर्‍याच एनक्रिप्शन योजना अस्तित्त्वात आहेत जे एन्क्रिप्टेड डेटावर संगणनास अनुमती देतात, त्यामध्ये मल्टी-पार्टी कंप्यूटेशन (एसएमएमसी), होमो-मोरफीक एन्क्रिप्शन (एफएचई / एसएचई) आणि फंक्शनल एन्क्रिप्शन (एफई) सुरक्षित आहेत. आम्ही येथे मल्टी-पार्टी कंप्यूटेशन (ट्यूटोरियल 5 मध्ये परिचय) वर लक्ष केंद्रित करू, ज्यात खाजगी अ‍ॅडिटीव्ह सामायिकरण समाविष्ट आहे आणि क्रिप्टो प्रोटोकॉल SecureNN आणि SPDZ वर अवलंबून आहे

या ट्यूटोरियलची अचूक सेटिंग खालीलप्रमाणे आहेः आपण सर्व्हर आहात याचा विचार करा आणि आपण आपल्या मॉडेलला $n$ वर्कर्सद्वारे ठेवलेल्या काही डेटावर प्रशिक्षण देऊ इच्छित आहात. सर्व्हर त्याचे गुप्त मॉडेल सामायिक करतो आणि प्रत्येक सामायिकर कामगारांना पाठवितो. कामगार त्यांचे डेटा देखील सामायिक करतात आणि त्या दरम्यान एक्सचेंज करतात. कॉन्फिगरेशनमध्ये आपण अभ्यास करू, तेथे 2 कामगार आहेतः alice आणि bob. समभागांची देवाणघेवाण केल्यानंतर, आता प्रत्येकाचे स्वतःचे एक शेअर्स आहेत, दुसर्‍या मजुरांचा एक भाग आहे आणि मॉडेलचा एक भाग आहे. योग्य क्रिप्टो प्रोटोकॉल वापरून मॉडेल्सना खाजगीरित्या प्रशिक्षण देणे आता संगणनास प्रारंभ होऊ शकते. एकदा मॉडेलचे प्रशिक्षण घेतल्यानंतर सर्व शेअर्स ते डिक्रिप्ट करण्यासाठी सर्व्हरवर परत पाठविले जाऊ शकतात. हे खालील आकृतीसह दर्शविले गेले आहे:

या प्रक्रियेचे उदाहरण देण्यासाठी, असे समजू या की alice आणि bob दोघांनी MNIST डेटासेटचा एक भाग धरला आणि अंकांचे वर्गीकरण करण्यासाठी मॉडेलला प्रशिक्षण देऊया!

लेखक:

अनुवादक/संपादक:

1. MNIST वरील एनक्रिप्टेड प्रशिक्षण डेमो

आयात आणि प्रशिक्षण कॉन्फिगरेशन


In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

import time

हा वर्ग प्रशिक्षणातील सर्व हायपर-पॅरामीटर्सचे वर्णन करतो. लक्षात ठेवा की ते सर्व येथे सार्वजनिक आहेत.


In [ ]:
class Arguments():
    def __init__(self):
        self.batch_size = 64
        self.test_batch_size = 64
        self.epochs = epochs
        self.lr = 0.02
        self.seed = 1
        self.log_interval = 1 # Log info at each batch
        self.precision_fractional = 3

args = Arguments()

_ = torch.manual_seed(args.seed)

PySyft आयात येथे आहेत. आम्ही दोन रिमोट कामगारांशी संपर्क साधतो ज्यांना आपण alice आणि bob म्हणूया आणि आपण crypto_provider नावाच्या दुसर्‍या कर्मचार्‍यास विनंती करतो जो आपल्याला आवश्यक असलेल्या सर्व crypto primitives देईल.


In [ ]:
import syft as sy  # import the Pysyft library
hook = sy.TorchHook(torch)  # hook PyTorch to add extra functionalities like Federated and Encrypted Learning

# simulation functions
def connect_to_workers(n_workers):
    return [
        sy.VirtualWorker(hook, id=f"worker{i+1}")
        for i in range(n_workers)
    ]
def connect_to_crypto_provider():
    return sy.VirtualWorker(hook, id="crypto_provider")

workers = connect_to_workers(n_workers=2)
crypto_provider = connect_to_crypto_provider()

प्रवेश आणि गुप्त सामायिकरण डेटा मिळवित आहे

येथे आम्ही एक युटिलिटी फंक्शन वापरत आहोत, जे खालील वर्तनचे अनुकरण करतो: आम्ही गृहित धरतो की MNIST डेटासेट आपल्या भागांपैकी एकाने धारण केलेल्या भागांमध्ये वितरित केले गेले आहे. कामगार मग त्यांचा डेटा बॅचमध्ये विभागतात आणि त्यांचा गुप्त डेटा एकमेकांमध्ये सामायिक करतात. अंतिम आयटम परत या गुप्त सामायिक बॅचची पुनरावृत्ती आहे, ज्यास आपण खाजगी डेटा लोडर (private data loader) म्हणतो. लक्षात ठेवा प्रक्रियेदरम्यान स्थानिक कर्मचार्‍यास (म्हणजे आपल्याला) डेटामध्ये कधीही प्रवेश नव्हता.

आपल्या नेहमीप्रमाणेच खासगी डेटासेटचे प्रशिक्षण आणि चाचणी घेतो आणि दोन्ही इनपुट आणि लेबल गुप्त सामायिक आहेत.


In [ ]:
def get_private_data_loaders(precision_fractional, workers, crypto_provider):
    
    def one_hot_of(index_tensor):
        """
        Transform to one hot tensor
        
        Example:
            [0, 3, 9]
            =>
            [[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
             [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
             [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]]
            
        """
        onehot_tensor = torch.zeros(*index_tensor.shape, 10) # 10 classes for MNIST
        onehot_tensor = onehot_tensor.scatter(1, index_tensor.view(-1, 1), 1)
        return onehot_tensor
        
    def secret_share(tensor):
        """
        Transform to fixed precision and secret share a tensor
        """
        return (
            tensor
            .fix_precision(precision_fractional=precision_fractional)
            .share(*workers, crypto_provider=crypto_provider, requires_grad=True)
        )
    
    transformation = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])
    
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data', train=True, download=True, transform=transformation),
        batch_size=args.batch_size
    )
    
    private_train_loader = [
        (secret_share(data), secret_share(one_hot_of(target)))
        for i, (data, target) in enumerate(train_loader)
        if i < n_train_items / args.batch_size
    ]
    
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data', train=False, download=True, transform=transformation),
        batch_size=args.test_batch_size
    )
    
    private_test_loader = [
        (secret_share(data), secret_share(target.float()))
        for i, (data, target) in enumerate(test_loader)
        if i < n_test_items / args.test_batch_size
    ]
    
    return private_train_loader, private_test_loader
    
    
private_train_loader, private_test_loader = get_private_data_loaders(
    precision_fractional=args.precision_fractional,
    workers=workers,
    crypto_provider=crypto_provider
)

मॉडेल तपशील

येथे आपण वापरत असलेले एक मॉडेल आहे, ते एक साधे आहे परंतु त्यांनी MNIST वर वाजवी प्रदर्शन केले आहे


In [ ]:
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 28 * 28)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

प्रशिक्षण आणि चाचणी कार्ये

प्रशिक्षण जवळजवळ नेहमीप्रमाणे केले जाते, वास्तविक फरक असा आहे की आम्ही नकारात्मक लॉग-संभाव्यता (negative log-likelihood)(PyTorch में F.nll_loss) सारख्या तोटा वापरू शकत नाही कारण SMPC द्वारे ही कार्ये पुनरुत्पादित करणे खूपच क्लिष्ट आहे. त्याऐवजी, आपण एक सोपा मीन स्क्वेअर एरर लॉस (mean square error loss) वापरतो.


In [ ]:
def train(args, model, private_train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(private_train_loader): # <-- now it is a private dataset
        start_time = time.time()
        
        optimizer.zero_grad()
        
        output = model(data)
        
        # loss = F.nll_loss(output, target)  <-- not possible here
        batch_size = output.shape[0]
        loss = ((output - target)**2).sum().refresh()/batch_size
        
        loss.backward()
        
        optimizer.step()

        if batch_idx % args.log_interval == 0:
            loss = loss.get().float_precision()
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}\tTime: {:.3f}s'.format(
                epoch, batch_idx * args.batch_size, len(private_train_loader) * args.batch_size,
                100. * batch_idx / len(private_train_loader), loss.item(), time.time() - start_time))

चाचणी कार्य बदलत नाही!


In [ ]:
def test(args, model, private_test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in private_test_loader:
            start_time = time.time()
            
            output = model(data)
            pred = output.argmax(dim=1)
            correct += pred.eq(target.view_as(pred)).sum()

    correct = correct.get().float_precision()
    print('\nTest set: Accuracy: {}/{} ({:.0f}%)\n'.format(
        correct.item(), len(private_test_loader)* args.test_batch_size,
        100. * correct.item() / (len(private_test_loader) * args.test_batch_size)))

चला प्रशिक्षण सुरू करूया!

येथे काय होत आहे याबद्दल काही नोट्स. प्रथम, आपण आपल्या कामगारांमध्ये मॉडेलची सर्व मापदंड सामायिक करतो. दुसरे, आपण ऑप्टिमाइझरच्या हायपरपॅमीटरला निश्चित अचूकतेमध्ये (fixed precision) रुपांतरित करतो. लक्षात घ्या की आपल्याला ते सामायिकपणे गुप्तपणे सामायिक करण्याची आवश्यकता नाही कारण ते आमच्या संदर्भात सार्वजनिक आहेत, परंतु गुप्त सामायिक मूल्ये मर्यादित फील्डमध्ये असल्याने आपल्याला वजनासारखे सातत्यपूर्ण ऑपरेशन करण्यासाठी, .fixed_precision वापरुन त्यांना मर्यादित क्षेत्रात हलविणे आवश्यक आहे. अद्यतनित करा $W \leftarrow W - \alpha * \Delta W$


In [ ]:
model = Net()
model = model.fix_precision().share(*workers, crypto_provider=crypto_provider, requires_grad=True)

optimizer = optim.SGD(model.parameters(), lr=args.lr)
optimizer = optimizer.fix_precision() 

for epoch in range(1, args.epochs + 1):
    train(args, model, private_train_loader, optimizer, epoch)
    test(args, model, private_test_loader)

आणि आता पहा! आपल्याला 100% एनक्रिप्टेड प्रशिक्षण वापरुन, MNIST डेटासेटचा एक छोटासा भाग वापरुन केवळ 75% अचूकता मिळेल!

2. चर्चा

आपण नुकतेच काय केले याचे विश्लेषण करून एन्क्रिप्टेड प्रशिक्षणाच्या सामर्थ्याकडे बारकाईने नजर टाकूया.

2.1 गणना करण्याची वेळ

पहिली गोष्ट म्हणजे उघडपणे चालू असलेला वेळ! आपण नक्कीच लक्षात घेतल्याप्रमाणे, हे साध्या मजकूर प्रशिक्षणापेक्षा खूपच हळू आहे. विशेषतः, 64 आयटमच्या 1 बॅचपेक्षा जास्त पुनरावृत्ती 3.2s घेते तर शुद्ध PyTorch मध्ये केवळ 13s. जरी हे ब्लॉकरसारखे वाटेल, परंतु फक्त येथेच लक्षात ठेवा की येथे सर्व काही दूरस्थपणे आणि एन्क्रिप्टेड जगात घडले आहे: कोणताही डेटा आयटम उघड केला गेला नाही. अधिक विशिष्ट म्हणजे, एका आयटमवर प्रक्रिया करण्याची वेळ 50ms आहे जे काही वाईट नाही. वास्तविक प्रश्न म्हणजे एनक्रिप्टेड प्रशिक्षण आवश्यक असते तेव्हा आणि जेव्हा केवळ एनक्रिप्टेड पूर्वानुमान पुरेसे असते तेव्हा विश्लेषण करणे होय. एक भविष्यवाणी करण्यासाठी 50ms मीटर उत्पादन-सज्ज परिस्थितीत पूर्णपणे स्वीकार्य आहे!

एक मुख्य अडचण महागड्या सक्रिय कार्याचा वापर आहे: एसएमपीसीसह relu activation खूप महाग आहे कारण ते खाजगी तुलना आणि SecureNN प्रोटोकॉल वापरते. स्पष्टीकरण म्हणून, जर आपण relu च्या जागी क्वाड्रॅटिक अ‍ॅक्टिवेशनसह बदलली, जसे की CryptoNets सारख्या एन्क्रिप्टेड संगणनावरील अनेक पेपर्समध्ये केली गेली आहे, तर आपण 3.2 से 1.2 पर्यंत खाली येऊ.

सामान्य नियम म्हणून, एनक्रिप्टकऱ्णासाठी काय आवश्यक आहे ते घेण्याचा मुख्य विचार आहे आणि हे ट्यूटोरियल आपल्याला हे कसे सोपे असू शकते ते दर्शविते.

2.2 SMPC सह Backpropagation

आपण मर्यादित क्षेत्रात पूर्णांकांसह कार्य करीत असले तरीही आपण आश्चर्य होऊ शक्ता की आम्ही बॅकप्रॉपॅगेशन आणि ग्रेडियंट अपडेट कसे करतो. हे करण्यासाठी, आम्ही ऑटोग्राडटेन्सर (AutogradTensor) नावाचे एक नवीन syft टेन्सर विकसित केले आहे. या ट्यूटोरियलने याचा मोठ्या प्रमाणावर वापर केला आहे, जरी आपण कदाचित तो पाहिले नसेल! चला मॉडेलचे वजन प्रिंट करू आणि त्याची चाचणी घेऊ:


In [ ]:
model.fc3.bias

आणि डेटा आयटम:


In [ ]:
first_batch, input_data = 0, 0
private_train_loader[first_batch][input_data]

आपण पाहू शकता की AutogradTensor तेथे आहे! हे torch wrapper आणि FixedPrecisionTensor यांच्यात असते ज्या दर्शविते की मूल्ये आता मर्यादित क्षेत्रात आहेत. एन्क्रिप्टेड मूल्यांवर ऑपरेशन्स केल्या जातात तेव्हा या AutogradTensor चे लक्ष्य संगणकीय आलेख संचयित करणे आहे. हे उपयुक्त आहे कारण बॅकप्रोपेगेशनसाठी बॅकवर्ड कॉल करतांना, हे AutogradTensor सर्व बॅकवर्ड फंक्शन्स अधिलिखित करतात जे एनक्रिप्टेड मोजणीशी सुसंगत नाहीत आणि या ग्रेडियंट्सची गणना कशी करतात हे दर्शविते. उदाहरणार्थ, बीव्हर ट्रिपल्स (Beaver triples) युक्तीचा गुणाकाराच्या संबंधात, गुणाकार विभक्त करणे खूप सोपे आहे की युक्ती आपण विभक्त करू इच्छित नाही: $\partial_b (a \cdot b) = a \cdot \partial b$. येथे आम्ही या ग्रेडियंट्सची गणना कशी करावी हे दर्शवितो:

class MulBackward(GradFunc):
    def __init__(self, self_, other):
        super().__init__(self, self_, other)
        self.self_ = self_
        self.other = other

    def gradient(self, grad):
        grad_self_ = grad * self.other
        grad_other = grad * self.self_ if type(self.self_) == type(self.other) else None
        return (grad_self_, grad_other)

आम्ही अधिक ग्रेडियंट्स कसे अंमलात आणले आहेत हे पाहणे आपल्यास उत्सुक असल्यास आपण tensors/interpreters/gradients.py वर नजर टाकू शकता.

गणना आलेखाच्या संदर्भात, याचा अर्थ असा आहे की आलेखची प्रत स्थानिक राहते आणि फॉरवर्ड पास समन्वयित करणारा सर्व्हर बॅकवर्ड पास कसा करावा याबद्दल सूचना प्रदान करते. आमच्या सेटिंगमधील ही एक अचूक वैध कल्पना आहे.

2.3 सुरक्षेची हमी

शेवटी, आपण येथे मिळवत असलेल्या सुरक्षिततेबद्दल काही सूचना देऊ: आपण येथे ज्या शत्रूंचा विचार करीत आहोत ते प्रामाणिक पण कुतूहल आहेत: याचा अर्थ असा आहे की एखादा विरोधी हा प्रोटोकॉल चालवून डेटाबद्दल काहीही शिकू शकत नाही, परंतु दुर्भावनापूर्ण विरोधी तरीही प्रोटोकॉलपासून विचलित करा आणि उदाहरणार्थ कंप्यूटेशनमध्ये तोडफोड करण्यासाठी शेअर्सला भ्रष्ट करण्याचा प्रयत्न करा. खासगी तुलनासह अशा SMPC संगणनांमध्ये दुर्भावनायुक्त विरोधकांविरूद्ध सुरक्षा अद्याप एक खुली समस्या आहे.

याव्यतिरिक्त, जरी सिक्युरिटी मल्टी-पार्टी कंप्यूटेशनने हे सुनिश्चित केले की प्रशिक्षण डेटावर प्रवेश केला गेला नाही, तरी साध्या मजकूर जगाकडून अनेक धोके अजूनही येथे आहेत. उदाहरणार्थ, आपण मॉडेलला विनंती करू शकता (MLaaS च्या संदर्भात), आपण अंदाज डेटाबेस प्राप्त करू शकता जे प्रशिक्षण डेटासेटबद्दल माहिती उघड करेल. विशेषत: आपल्याला सदस्यता हल्ल्यांपासून (membership attacks) संरक्षण नाही, मशीन लर्निंग सेवांवर सामान्य हल्ला जेथे डेटासेटमध्ये एखादी विशिष्ट वस्तू वापरली गेली होती की नाही हे निर्धारित करणे आवश्यक आहे. याशिवाय, अनावश्यक स्मृती प्रक्रिया (डेटा आयटमबद्दल विशिष्ट वैशिष्ट्य शिकणारी मॉडेल्स), मॉडेल इनव्हर्जन किंवा माहिती काढणे यासारखे अन्य हल्ले अद्याप शक्य आहेत.

वर नमूद केलेल्या बर्‍याच धमक्यांसाठी प्रभावी असलेले एक सामान्य निराकरण म्हणजे डिफेरेन्शिअल प्रायव्हसी (Differential Privacy) समाविष्ट करणे. हे सिक्युर मल्टी-पार्टी कंप्यूटेशनसह एकत्र केले जाऊ शकते आणि अतिशय मनोरंजक सुरक्षा हमी प्रदान करू शकते. आम्ही सध्या बर्‍याच अंमलबजावणींवर काम करीत आहोत आणि लवकरच अशा दोन्ही उदाहरणांची सांगड घालण्याचे एक उदाहरण देण्याची आशा आहे!

निष्कर्ष

जसे आपण पाहिले आहे की SMPC वापरुन मॉडेलचे प्रशिक्षण देणे कोडच्या दृष्टिकोनातून गुंतागुंत नाही, जरी आम्ही टोपीच्या खाली जटिल वस्तू वापरतो. हे लक्षात घेऊन, आपण आता आपल्या वापराच्या प्रकरणांचे विश्लेषण केले पाहिजे की प्रशिक्षणासाठी किंवा मूल्यांकनासाठी एन्क्रिप्टेड संगणना कधी आवश्यक आहे. जर एन्क्रिप्टेड गणना सामान्यत: हळू असेल तर त्याचा वापर काळजीपूर्वक देखील केला जाऊ शकतो जेणेकरून एकूण गणना कमी होईल.

आपण याचा आनंद घेत असल्यास आणि एआय(AI) आणि एआय सप्लाय चेन (डेटा) च्या विकेंद्रित(Decentralized) मालकीच्या गोपनीयतेच्या संरक्षणाच्या दिशेने चळवळीत सामील होऊ इच्छित असाल तर आपण हे खालील प्रकारे करू शकता!

Pysyft ला Github वर Star करा!

आमच्या समुदायाला मदत करण्याचा सर्वात सोपा मार्ग म्हणजे फक्त गिटहब(GitHub) रेपो(Repo) तारांकित(Star) करणे! हे आम्ही तयार करीत असलेल्या छान साधनांविषयी जागरूकता वाढविण्यास मदत करते.

आमच्या Slack मध्ये सामील व्हा!

नवीनतम प्रगतीवर अद्ययावत राहण्याचा उत्तम मार्ग म्हणजे आमच्या समुदायामध्ये सामील होणे! आपण http://slack.openmined.org येथे फॉर्म भरुन तसे करू शकता.

एका कोड प्रोजेक्टमध्ये सामील व्हा!

आमच्या समुदायामध्ये योगदानाचा उत्तम मार्ग म्हणजे कोड योगदानकर्ता बनणे! कोणत्याही वेळी आपण (PySyft GitHub Issues Page) वर जाऊ शकता आणि "Project" साठी फिल्टर करू शकता. हे आपण कोणत्या प्रकल्पांमध्ये सामील होऊ शकता याबद्दल विहंगावलोकन देणारी सर्व उच्च स्तरीय तिकिटे दर्शवेल! आपण एखाद्या प्रकल्पात सामील होऊ इच्छित नसल्यास, परंतु आपण थोडं कोडिंग करू इच्छित असाल तर आपण good first issues म्हणून चिन्हांकित गिटहब(GitHub) अंक शोधून आणखी "one off" मिनी-प्रकल्प(mini project) शोधू शकता.

दान करा

आपल्याकडे आमच्या कोडेबेसमध्ये योगदान देण्यास वेळ नसल्यास, परंतु तरीही आपल्याला समर्थन द्यावयाचे असल्यास आपण आमच्या मुक्त संग्रहात बॅकर देखील होऊ शकता. सर्व देणगी आमच्या वेब होस्टिंग आणि हॅकॅथॉन आणि मेटअप्स सारख्या इतर सामुदायिक खर्चाकडे जातात!

OpenMined's Open Collective Page


In [ ]: